//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
//==================================================================

#include "stdafx.h"
#include "gxy.h"
#include "gxy_font.h"

//==================================================================
namespace GXY
{

//==================================================================
#define CHAR_ASCII_MIN	32
#define CHAR_ASCII_MAX	126
#define CHAR_ASCII_N	(CHAR_ASCII_MAX - CHAR_ASCII_MIN + 1)

//==================================================================
static void setup_font_spacing_and_map( font_t *fop, int is_prop, const TCHAR *custom_map, int custom_map_len )
{
	int max_w = fop->si->GetMaxSpriteWidth();
	int	max_h = fop->si->GetMaxSpriteHeight();

	fop->fix_w = max_w;
	fop->fix_h = max_h;

	if ( is_prop )
		fop->spc_w = max_w / 2;
	else
		fop->spc_w = max_w;

	fop->spc_h = max_h;

	if ( custom_map_len && custom_map )
	{
		if NOT( fop->custom_mapp = (u_char *)PSYS_CMALLOC( sizeof(u_char) * CHAR_ASCII_N ) )
			return;	// error ! the assert inside the malloc will notify of errors

		for (int i=0; i < custom_map_len; ++i)
		{
			PASSERT( custom_map[i] >= CHAR_ASCII_MIN && custom_map[i] <= CHAR_ASCII_MAX );
			if ( custom_map[i] >= CHAR_ASCII_MIN && custom_map[i] <= CHAR_ASCII_MAX )
			{
				fop->custom_mapp[ custom_map[i] - CHAR_ASCII_MIN ] = i;
			}
		}
	}
}

//==================================================================
void MLoadFontBegin( const TCHAR *fnamep )
{
	PASSERT( _ctxp->_mload_si == 0 );
	_ctxp->_mload_si = new SpriteManager( fnamep );
	PASSERT( _ctxp->_mload_si != 0 );
}

//==================================================================
void MLoadFontEnd()
{
	_ctxp->_mload_si = 0;
}

//==================================================================
void MLoadFontNext(
	int font_idx,
	int cell_w,
	int cell_h,
	int offx,
	int offy,
	int is_prop,
	int			*y_corr_map,
	const TCHAR	 *custom_map,
	int custom_map_len
)
{
	PASSERT( _ctxp->_font_use_table[font_idx] == FONT_USE_EMPTY );
	if NOT( _ctxp->_font_use_table[font_idx] == FONT_USE_EMPTY )
		return;

	font_t	*fop = &_ctxp->_font[ font_idx ];

	SAFE_DELETE( fop->si );

	fop->si = new SpriteManager( _ctxp->_mload_si );
	fop->si->BuildSpritesFromSize( cell_w, cell_h, offx, offy, 0, 0, 255, 255, custom_map_len, true );

	setup_font_spacing_and_map( fop, is_prop, custom_map, custom_map_len  );

	if ( y_corr_map )
	{
		for (int i=0; i < custom_map_len; ++i)
		{
			fop->si->GetSprite(i)->SetDrawOffset( 0, y_corr_map[i] );
		}
	}

	_ctxp->_font_use_table[ font_idx ] = FONT_USE_ASCII;
}

//==================================================================
void LoadFont(
	int font_idx,
	const TCHAR* fnamep,
	int cell_w,
	int cell_h,
	int offx,
	int offy,
	int is_prop,
	const TCHAR	*custom_map,
	int custom_map_len
)
{
	PASSERT( _ctxp->_font_use_table[font_idx] == FONT_USE_EMPTY );
	if NOT( _ctxp->_font_use_table[font_idx] == FONT_USE_EMPTY )
		return;

	font_t	*fop = &_ctxp->_font[ font_idx ];

	SAFE_DELETE( fop->si );

	fop->si = new SpriteManager( fnamep );
	fop->si->BuildSpritesFromSize( cell_w, cell_h, offx, offy, 0, 0, 255, 255, 255, true );

	setup_font_spacing_and_map( fop, is_prop, custom_map, custom_map_len  );

	_ctxp->_font_use_table[ font_idx ] = FONT_USE_ASCII;
}
/*
//==================================================================
// make a new font using the texture from another font
void glt_font_copy_ofs(
	int des_font_idx,
	int src_font_idx,
	int cell_w,
	int cell_h,
	int offx,
	int offy,
	int is_prop
)
{
	PASSERT( _ctxp->_font_use_table[ src_font_idx ] == FONT_USE_ASCII );
	if NOT( _ctxp->_font_use_table[ src_font_idx ] == FONT_USE_ASCII )
		return;

font_t	*sfop = &_ctxp->_font[ src_font_idx ];
font_t	*dfop = &_ctxp->_font[ des_font_idx ];

	if( dfop->si )
	{
		slist_free( dfop->si );
		dfop->si = 0;
	}

	// make sure the source font is present !
	PASSERT( sfop->si );

	// copy all the generic data
	*dfop = *sfop;

	dfop->custom_mapp = NULL;

	// build a new slist
	dfop->si = slist_new_copy( sfop->si );
	slist_build_items_from_size( cell_w, cell_h, offx, offy, 0, 0, 255, 255, 255, SLIST_BUILD_FLG_FIT );
/*
	if ( sfop->custom_map_len && sfop->custom_mapp )
	{
		if NOT( dfop->custom_mapp = PSYS_MALLOC( sizeof(u_char) * sfop->custom_map_len ) )
			return;	// error ! the assert inside the malloc will notify of errors

		dfop->custom_map_len = sfop->custom_map_len;

		memcpy( dfop->custom_mapp, sfop->custom_mapp, sizeof(u_char) * sfop->custom_map_len );
	}
* /

	_ctxp->_font_use_table[ des_font_idx ] = _ctxp->_font_use_table[ src_font_idx ];
}
*/
//==================================================================
void LoadFont( int font_idx, const TCHAR *fnamep, int cell_w, int cell_h, int is_prop )
{
	PASSERT( _ctxp->_font_use_table[font_idx] == FONT_USE_EMPTY );
	if NOT( _ctxp->_font_use_table[font_idx] == FONT_USE_EMPTY )
		return;

	LoadFont( font_idx, fnamep, cell_w, cell_h, 0, 0, is_prop, 0, 0 );
}

//==================================================================
float getFontHeight_a(void)
{
	font_t	*fop = &_ctxp->_font[ _ctxp->_sys_state._font_idx ];
	if ( _ctxp->_sys_state._font_idx >= 0 )
		if ( fop->si )
		{
			return fop->spc_h * _ctxp->_sys_state._sca_y * _ctxp->_sys_state._font_sca_y;
		}

	PASSERT( 0 );
	return 0;
}

//==================================================================
float GetFontHeight(void)
{
	switch ( _ctxp->_font_use_table[ _ctxp->_sys_state._font_idx ] )
	{
	case FONT_USE_ASCII:	return getFontHeight_a();
	}

	PASSERT( 0 );
	return 0;
}

//==================================================================
float getCharWidth_a( int ch )
{
Sprite	*sip;
font_t	*fop = &_ctxp->_font[ _ctxp->_sys_state._font_idx ];
int		idx;

	if ( _ctxp->_sys_state._font_idx >= 0 )
		if ( fop->si )
		{
			idx = ((u_int)ch) - CHAR_ASCII_MIN;
			if ( fop->custom_mapp )
				idx = fop->custom_mapp[ idx ];
		
			sip = fop->si->GetSprite( idx );
			return sip->_w * _ctxp->_sys_state._sca_x * _ctxp->_sys_state._font_sca_x;
		}

	return 0;
}

//==================================================================
float GetCharWidth( int ch )
{
	switch ( _ctxp->_font_use_table[ _ctxp->_sys_state._font_idx ] )
	{
	case FONT_USE_ASCII:	return getCharWidth_a( ch );

	default:
	case FONT_USE_EMPTY:
		PASSERT( 0 );
		return 0;
	}
}

//==================================================================
float calcStringWidth_a( const TCHAR *strp )
{
Sprite			*sip;
SpriteManager	*spritep;
int				idx;
int				w;
font_t			*fop = &_ctxp->_font[ _ctxp->_sys_state._font_idx ];

	if ( _ctxp->_sys_state._font_idx < 0 )
		return 0;

	if NOT( _ctxp->_sys_state._font_is_prop )
	{
		return fop->fix_w * _tcslen( strp );
	}
	else
	{
		w = 0;
		spritep = fop->si;
		while ( *strp != '\0' )
		{
			if (*strp == ' ')
				w += fop->spc_w;
			else
			{
				idx = ((u_int)*strp) - CHAR_ASCII_MIN;
				if ( fop->custom_mapp )
					idx = fop->custom_mapp[ idx ];

				if ( sip = fop->si->GetSprite( idx ) )
				{
					w += sip->_w;
				}
				else
					w += fop->spc_w;
			}

			strp++;
		}
		return w * _ctxp->_sys_state._sca_x * _ctxp->_sys_state._font_sca_x;
	}

	return 0;
}

//==================================================================
float CalcStringWidth( const TCHAR *strp )
{
	switch ( _ctxp->_font_use_table[ _ctxp->_sys_state._font_idx ] )
	{
	case FONT_USE_ASCII:	return calcStringWidth_a( strp );

	default:
	case FONT_USE_EMPTY:
		PASSERT( 0 );
		return 0;
	}
}

//==================================================================
static const TCHAR *str_getrowlen( const TCHAR *strp )
{
	int	i		= 0;

	if ( *strp == 0 )
		return NULL;

	while ( 1 )
	{
		if ( *strp == '\n' )
			return strp + 1;
		else
		if ( *strp == 0 )
			return strp;
		else
		{
			++strp;
		}
	}
}

//==================================================================
static const TCHAR *str_getrow( const TCHAR *strp, TCHAR out_buff[], int out_buff_maxlen )
{
	int	i		= 0;
	out_buff[0] = 0;

	if ( *strp == 0 )
		return NULL;

	while ( 1 )
	{
		if ( *strp == _TXCHAR('\n') )
			return strp + 1;
		else
		if ( *strp == 0 )
			return strp;
		else
		{
			if ( i >= out_buff_maxlen-1 )
			{
				PASSERT( 0 );
				return NULL;
			}

			out_buff[ i++ ] = *strp++;
			out_buff[ i ] = 0;
		}
	}
}

//==================================================================
void CalcDrawStringSize( const TCHAR *strp, float &out_width, float &out_height )
{
	const TCHAR *strp2 = strp;

	TCHAR		buff[4096];
	float		cur_wd;

	out_width = 0;
	out_height = 0;
	while ( strp2 = str_getrow( strp2, buff, _countof(buff) ) )
	{
		switch ( _ctxp->_font_use_table[ _ctxp->_sys_state._font_idx ] )
		{
		case FONT_USE_ASCII:
			cur_wd = calcStringWidth_a( buff );
			break;

		default:
		case FONT_USE_EMPTY:
			PASSERT( 0 );
			return;
		}

		if ( cur_wd > out_width )
			out_width = cur_wd;

		out_height += GetFontHeight();
	}
}

//==================================================================
static int count_rows( const TCHAR *strp )
{
	int	rows = 0;

	while ( strp = str_getrowlen( strp ) )
	{
		++rows;
	}

	return rows;
}

//==================================================================
void DrawStringRectAlign( const Rect &rc,
								 int align_x,
								 int align_y,
								 const TCHAR *strp )
{
	DrawStringRectAlign( rc._x, rc._y, rc._w, rc._h, align_x, align_y, strp );
}

//==================================================================
void DrawStringRectAlign( float rcx, float rcy,
							     float rcw, float rch,
								 int align_x,
								 int align_y,
								 const TCHAR *strp )
{
void (*draw_func)( const TCHAR *strp, float x, float y, int align_x, int align_y );

	switch ( _ctxp->_font_use_table[ _ctxp->_sys_state._font_idx ] )
	{
	case FONT_USE_EMPTY:	PASSERT( 0 );						return;
	case FONT_USE_ASCII:	draw_func = drawString_a;		break;
	}

	const TCHAR *strp2 = strp;
	TCHAR		buff[4096];

//	int		n_rows = count_rows( strp );
//	float	str_he = n_rows * font_he;

	float	str_wd, str_he;
	CalcDrawStringSize( strp, str_wd, str_he );

	Rect	outrect( rcx, rcy, rcw, rch );
	Rect	textrect( 0, 0, str_wd, str_he );

	textrect.PositionInside( outrect, align_x, align_y );

	// render
	float	font_he = GetFontHeight();
	while ( strp2 = str_getrow( strp2, buff, _countof(buff) ) )
	{
		draw_func( buff, textrect._x, textrect._y, -1, -1 );
		textrect._y += font_he;
	}
}

//==================================================================
void SetFont( int font_idx )
{
	PASSERT( font_idx == -1 || font_idx >= 0 && font_idx < GXY_MAX_FONTS );
	_ctxp->_sys_state._font_idx = font_idx;
}

//==================================================================
void SetFontScale( float sca_x, float sca_y )
{
	_ctxp->_sys_state.SetFontScale( sca_x, sca_y );
}

//==================================================================
void SetFontProportional( int onoff )
{
	_ctxp->_sys_state._font_is_prop = onoff;
}

//==================================================================
void drawString_a( const TCHAR *strp, float x, float y, int align_x, int align_y )
{
int			i, idx;
int			len;
Sprite		*sip;
float		w, h;
float		sca_x = _ctxp->_sys_state._sca_x * _ctxp->_sys_state._font_sca_x;
float		sca_y = _ctxp->_sys_state._sca_y * _ctxp->_sys_state._font_sca_y;

	int font_idx	= _ctxp->_sys_state._font_idx;
	PASSERT( font_idx >= 0 && font_idx < GXY_MAX_FONTS );

	int font_prop	= _ctxp->_sys_state._font_is_prop;
	font_t	*fop	= &_ctxp->_font[ font_idx ];

	h = 0;
	w = 0;
	len = (int)_tcslen( strp );
	for (i=0; i < len; ++i)
	{
		if ( strp[i] == ' ' )
		{
			w += fop->spc_w;
			h = fop->spc_h;
		}
		else
		{
			idx = strp[i] - CHAR_ASCII_MIN;
			PASSERT( idx >= 0 && idx < CHAR_ASCII_N );
			if ( fop->custom_mapp )
				idx = fop->custom_mapp[ idx ];

			if ( font_prop )
			{
				if NOT( sip = fop->si->GetSprite( idx ) )
				{
					w += fop->spc_w;
					h = fop->spc_h;
				}
				else
				{
					w += sip->_w;
					h = sip->_h;	// assume they all have the same height
				}
			}
			else
			{
				w += fop->fix_w;
				h = fop->fix_h;
			}
		}
	}

	w *= sca_x;
	h *= sca_y;

	if ( align_x == 0 )	x -= w / 2;		else	// align center
	if ( align_x > 0 )	x -= w;					// align right

	if ( align_y == 0 )	y -= h / 2;		else	// align middle
	if ( align_y > 0 )	y -= h;					// align bottom

	for (i=0; i < len; ++i)
	{
		if ( strp[i] == ' ' )
		{
			x += fop->spc_w * sca_x;
		}
		else
		{
			idx = strp[i] - CHAR_ASCII_MIN;
			PASSERT( idx >= 0 && idx < CHAR_ASCII_N );
			if ( fop->custom_mapp )
				idx = fop->custom_mapp[ idx ];

			if ( sip = fop->si->GetSprite( idx ) )
				DrawSprite( sip, floorf(x), floorf(y), _ctxp->_sys_state._font_sca_x, _ctxp->_sys_state._font_sca_y );

			if ( font_prop )
			{
				if NOT( sip )
					x += fop->spc_w * sca_x;
				else
					x += sip->_w * sca_x;
			}
			else
				x += fop->fix_w * sca_x;
		}
	}
}

//==================================================================
void DrawStringAlign( float x, float y, int align_x, int align_y, const TCHAR *strp )
{
	switch ( _ctxp->_font_use_table[ _ctxp->_sys_state._font_idx ] )
	{
	case FONT_USE_EMPTY:	PASSERT( 0 );	return;
	case FONT_USE_ASCII:	drawString_a( strp, x, y, align_x, align_y ); break;
	}
}

//==================================================================
void DrawString( float x, float y, const TCHAR *strp )
{
	DrawStringAlign( x, y, -1, -1, strp );
}

//==================================================================
void DrawStringF_vs( float px, float py, const TCHAR *strp, va_list va )
{
	TCHAR	buff[4096];

	_vstprintf_s( buff, strp, va );
	DrawStringAlign( px, py, -1, -1, buff );
}

//==================================================================
void DrawStringF( float px, float py, const TCHAR *strp, ... )
{
	va_list	va;
	va_start( va, strp );
		DrawStringF_vs( px, py, strp, va );
	va_end( va );
}

//==================================================================
void DrawStringAlignF( float px, float py, int align_x, int align_y, const TCHAR *strp, ... )
{
va_list	va;
TCHAR	buff[4096];

	va_start( va, strp );
		_vstprintf_s( buff, strp, va );
		DrawStringAlign( px, py, align_x, align_y, buff );
	va_end( va );
}

//==================================================================
void glt_draw_stringf_boxalign( float px, float py, int align_x, int align_y, const TCHAR *strp, ... )
{
va_list	va;
TCHAR	buff[4096];

	va_start( va, strp );
		_vstprintf_s( buff, strp, va );
		DrawStringAlign( px, py, align_x, align_y, buff );
	va_end( va );
}

//==================================================================
void GetDrawStringPixelSize( float out_size[2] )
{
	out_size[0] = _ctxp->_sys_state._sca_x * _ctxp->_sys_state._font_sca_x;
	out_size[1] = _ctxp->_sys_state._sca_y * _ctxp->_sys_state._font_sca_y;
}

};